package ws

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"svr/src/conf"
	"svr/src/db"
	"svr/src/eor"
	"svr/src/logs"
	"svr/src/msg"
	"svr/src/xutil"
	"sync"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/gorilla/websocket"
	"google.golang.org/protobuf/proto"
)

// ----这里主要是维护每个设备上下行链接的管理相关----------------------------------------------------------
type DeviceInfo struct {
	Type         msg.DeviceType
	AddrConnect  string
	AddrListen   string //监听端口IP，这个值也用来维护每个设备的唯一识别码，客户端用AddrConnect直接赋值给AddrListen
	Conn         *websocket.Conn
	FromDown     bool
	l            sync.RWMutex
	TimeOutStamp time.Time //收到消息时，增加此设备的延迟时间
	Onlink       bool
	ClientCount  int32
	Blance       msg.BalanceType
}

func createDeviceInfo(conn *websocket.Conn, fromDown bool) *DeviceInfo {
	di := &DeviceInfo{
		Type:         msg.DeviceType_Unknown,
		AddrConnect:  conn.RemoteAddr().String(),
		Conn:         conn,
		FromDown:     fromDown,
		TimeOutStamp: time.Now().Add(DurHeartTimeout),
		Onlink:       true,
		ClientCount:  0,
		Blance:       msg.BalanceType_None,
	}
	if fromDown == false { //连接的是上级服务器
		di.AddrListen = di.AddrConnect
	}
	return di
}

func (di *DeviceInfo) SetListenPort(port int32) {
	ip := getIpFromAddr(di.AddrConnect)
	di.AddrListen = fmt.Sprintf(ip+":%d", port)
}
func (di *DeviceInfo) Close() {
	// delAddrDeviceInfo(di.AddrConnect) //从地址表删除 !#dv
	removeDeviceFromMapGroup(di) //从类型表删除
	di.Conn.Close()
	di.Onlink = false
}

func getCilentIpRdsKey(clientIp string, devType msg.DeviceType) string {
	return db.GetRedisKey("client", clientIp, "svr", int(devType))
}

const epos_AddClientIpToRedis = "DeviceInfo|AddClientIpToRedis"

func (di *DeviceInfo) AddClientIpToRedis(clientIp string) {
	rds := db.GetRedis()
	k := getCilentIpRdsKey(clientIp, di.Type)
	err := rds.Set(db.GetCtx(), k, di.AddrListen, db.RdsDur).Err()
	if err != nil {
		_ = eor.NewErr(msg.ErrCode_Err_Redis, epos_AddClientIpToRedis, "1", err)
	}
}

const epos_DelClientIpToRedis = "DeviceInfo|DelClientIpToRedis"

func (di *DeviceInfo) DelClientIpToRedis(clientIp string) {
	rds := db.GetRedis()
	k := getCilentIpRdsKey(clientIp, di.Type)
	err := rds.Del(db.GetCtx(), k).Err()
	if err != nil {
		_ = eor.NewErr(msg.ErrCode_Err_Redis, epos_DelClientIpToRedis, "1", err)
	}
}

const epos_GetDeviceByClientIp = "DeviceInfo|GetDeviceByClientIp"

func GetDeviceByClientIp(devType msg.DeviceType, clientIp string) (*DeviceInfo, *msg.Error) {
	rds := db.GetRedis()
	k := getCilentIpRdsKey(clientIp, devType)
	str, err := rds.Get(db.GetCtx(), k).Result()
	if err != nil {
		if err == redis.Nil {
			//!#todo寻找ip
		}
		return nil, eor.NewErr(msg.ErrCode_Err_Redis, epos_GetDeviceByClientIp, "1", err)
	}
	return GetConnectDeviceByIp(devType, str)
}

const epos_SendMessageToClient = "SendMessageToClient"

func SendMessageToClient(m proto.Message, clientTyp msg.DeviceType, clientIp string) *msg.Error {
	di, mer := GetDeviceByClientIp(clientTyp, clientIp)
	if mer != nil {
		return mer
	}
	cfg := &msg.MsgConfig{
		Name:   GetMessageName(m),
		Source: conf.ServerConfig.ServerType,
		Dest:   clientTyp,
		DestIp: clientIp,
	}
	bs, err := proto.Marshal(m)
	if err != nil {
		return eor.NewErr(msg.ErrCode_Err_Marshal, epos_SendMessageToClient, "1", err)
	}
	mi := &MsgInfo{
		Conf: cfg,
		Data: bs,
	}
	return di.SendMessageData(mi.ToBytes())
}

const epos_ReadMessage = "DeviceInfo|ReadMessage"

func (di *DeviceInfo) ReadMessage() *msg.Error {
	typ, bs, err := di.Conn.ReadMessage()
	if err != nil {
		return eor.NewErrSafed(msg.ErrCode_Err_ReadData, epos_ReadMessage, "1", err)
	}
	if typ == websocket.BinaryMessage {
		mi, mer := readMsgInfo(bs)
		if mer != nil {
			return mer
		}
		if mi.CheckError() {
			return nil
		}
		if mi.IsSelfMsg() { //是自己要处理的消息
			if !checkTaskWait(mi) { //不是等待中的消息
				AddMsgTask(&MsgTask{
					Dev: di,
					Msg: mi,
				}) //任务扔过去处理就不用管了
			}
		} else { //目标服务器不是自己
			mer = mi.SendMsgToDest() //转发走
			if mer != nil {
				//!#todo
			}
		}
	} else { //其他类型消息，测试用 !#!#!#!#
		str := string(bs)
		log.Println(typ, str) //其他类型的消息，暂不做处理
		str += "???"
		di.Conn.WriteMessage(1, []byte(str))
	}
	return nil
}

// 从byte[]里读出来msgInfo
var emp_readMsgInfo = "readMsgInfo"

func readMsgInfo(bs []byte) (*MsgInfo, *msg.Error) {
	// logs.Debugf("readMsgInfo :%+v", bs)
	rb := xutil.NewReaderBuf(bs)
	mi := &MsgInfo{}
	bs0, err := rb.ReadBytes() //msg config
	if err != nil {
		return nil, eor.NewErr(msg.ErrCode_Err_ReadData, emp_readMsgInfo, "1", err)
	}
	m := &msg.MsgConfig{}
	err = proto.Unmarshal(bs0, m)
	if err != nil {
		return nil, eor.NewErr(msg.ErrCode_Err_Unmarshal, emp_readMsgInfo, "2", err)
	}
	bs1, err := rb.ReadBytes() //正式的msg的[]byte
	if err != nil {
		return nil, eor.NewErr(msg.ErrCode_Err_ReadData, emp_readMsgInfo, "3", err)
	}
	mi.Conf = m
	// logs.Debugf("msg conf: %+v", mi.Conf)
	mi.Data = bs1
	return mi, nil
}

const epos_SendMessageData = "DeviceInfo|SendMessageData"

func (di *DeviceInfo) SendMessageData(data []byte) *msg.Error {
	// logs.Debugf("------SendMessageData----:%+v", data)
	di.l.Lock()
	defer di.l.Unlock()
	err := di.Conn.WriteMessage(2, data)
	if err != nil {
		return eor.NewErr(msg.ErrCode_Err_WriteData, epos_SendMessageData, "1", err, data)
	}
	return nil
}

const epos_SendMessageDirect = "DeviceInfo|SendMessageDirect"

func (di *DeviceInfo) SendMessageDirect(m proto.Message) (*msg.MsgConfig, *msg.Error) {
	cfg := &msg.MsgConfig{
		Name:    GetMessageName(m),
		Source:  conf.ServerConfig.ServerType,
		Dest:    msg.DeviceType_JustDispose,
		MsgCode: xutil.UniqueInt64(), //message的唯一标识，需要同步等数据的时候用到
	}
	bs, err := proto.Marshal(m)
	if err != nil {
		return cfg, eor.NewErr(msg.ErrCode_Err_Marshal, epos_SendMessageDirect, "1", err, m)
	}
	mi := &MsgInfo{
		Conf: cfg,
		Data: bs,
	}
	return cfg, di.SendMessageData(mi.ToBytes())
}

const epos_ReturnMsg = "DeviceInfo|ReturnMsg"

func (di *DeviceInfo) ReturnMsg(ma proto.Message, mer *msg.Error, mt *MsgTask) *msg.Error {
	mc0 := mt.Msg.Conf
	mc1 := &msg.MsgConfig{
		Name:     GetMessageName(ma),
		Source:   conf.ServerConfig.ServerType,
		Dest:     mc0.Source,
		MsgCode:  mc0.MsgCode,
		SourceIp: mc0.DestIp,
		DestIp:   mc0.SourceIp,
		Err:      mer,
	}

	mi := &MsgInfo{
		Conf: mc1,
	}
	if mer == nil {
		bs, err := proto.Marshal(ma)
		if err != nil {
			return eor.NewErr(msg.ErrCode_Err_Marshal, epos_SendMessageDirect, "1", err, ma)
		}
		mi.Data = bs
	}
	return di.SendMessageData(mi.ToBytes())
}

// func (di *DeviceInfo) ReturnError(ma proto.Message, err *msg.Error, mt *MsgTask) *msg.Error {
// 	mc0 := mt.Msg.Conf
// 	mc1 := &msg.MsgConfig{
// 		Name:     GetMessageName(ma),
// 		Source:   conf.ServerConfig.ServerType,
// 		Dest:     mc0.Source,
// 		MsgCode:  mc0.MsgCode,
// 		SourceIp: mc0.DestIp,
// 		DestIp:   mc0.SourceIp,
// 	}
// 	mi := &MsgInfo{
// 		Conf: mc1,
// 		Data: nil,
// 	}
// 	return di.SendMessageData(mi.ToBytes())
// }

// 发消息等回信触发回调
func (di *DeviceInfo) SendMessageAndWait(mr proto.Message, ma proto.Message, call func(ma proto.Message, err *msg.Error)) *msg.Error {
	cfg, err := di.SendMessageDirect(mr)
	if err != nil {
		return err
	}
	addTaskWait(cfg.MsgCode, ma, call)
	return nil
}

// -----------------------------------------------------------------------------------------------------
/**    //!#dv
var mapAddrToDev *xutil.Map //地址表[string,*DeviceInfo]，所有的地址对应的设备表，当前链接，上下行都有

func addAddrDeviceInfo(conn *websocket.Conn, fromDown bool) (*DeviceInfo, error) {
	if mapAddrToDev == nil {
		mapAddrToDev = xutil.CreateMap()
	}
	addr := conn.RemoteAddr().String()
	if !mapAddrToDev.ContainsKey(addr) {
		dev := createDeviceInfo(conn, fromDown)
		mapAddrToDev.Set(addr, dev)
		return dev, nil
	} else {
		return nil, fmt.Errorf("conn :%s is binded ", addr)
	}
}

func getAddrDeviceInfo(addr string) *DeviceInfo {
	v := mapAddrToDev.Get(addr)
	if v != nil {
		return v.(*DeviceInfo)
	}
	return nil
}

func delAddrDeviceInfo(addr string) bool {
	if mapAddrToDev != nil { //从地址表删除
		if mapAddrToDev.ContainsKey(addr) {
			mapAddrToDev.Delete(addr)
			return true
		}
	}
	return false
}
/**/
// -----------------------------------------------------------------------------------------------------
type DeviceGroup struct {
	Type  msg.DeviceType
	Devs  map[string]*DeviceInfo
	Index int
	l     sync.RWMutex
}

func createDeviceGroup(di *DeviceInfo) *DeviceGroup {
	dg := &DeviceGroup{
		Type:  di.Type,
		Devs:  make(map[string]*DeviceInfo),
		Index: 0,
	}
	dg.AddDevice(di)
	return dg
}
func (dg *DeviceGroup) CheckTimeOut(now time.Time, call func(*DeviceInfo)) {
	if dg.Devs != nil {
		for _, di := range dg.Devs {
			if now.After(di.TimeOutStamp) {
				call(di)
			}
		}
	}
}

func (dg *DeviceGroup) AddDevice(di *DeviceInfo) error {
	dg.l.Lock()
	defer dg.l.Unlock()
	dg.Devs[di.AddrListen] = di
	return nil
}
func (dg *DeviceGroup) RemoveDevice(di *DeviceInfo) (int, bool) {
	dg.l.Lock()
	defer dg.l.Unlock()
	_, ok := dg.Devs[di.AddrListen]
	if ok {
		delete(dg.Devs, di.AddrListen)
	}
	return len(dg.Devs), ok
}

const epos_GetDeviceToSend = "DeviceGroup|GetDeviceToSend"

func (dg *DeviceGroup) GetDeviceToSend(useBalance bool) (*DeviceInfo, *msg.Error) {
	ct := len(dg.Devs)
	if ct <= 0 {
		return nil, eor.NewErr(msg.ErrCode_Err_FindData, epos_GetDeviceToSend, "1", "DeviceGroup :", dg.Type, " have not device.")
	}
	if ct == 1 {
		for _, di := range dg.Devs {
			return di, nil
		}
	}
	dg.l.Lock()
	defer dg.l.Unlock()
	if useBalance {
		var dev *DeviceInfo
		for _, di := range dg.Devs {
			if dev == nil {
				dev = di
			} else {
				if dev.ClientCount > di.ClientCount {
					dev = di
				}
			}
		}
		return dev, nil
	} else {
		dg.Index++
		if dg.Index >= ct {
			dg.Index = 0
		}
		i := 0
		for _, di := range dg.Devs {
			if i == dg.Index {
				return di, nil
			}
			i++
		}
		return nil, eor.NewErr(msg.ErrCode_Err_FindData, epos_GetDeviceToSend, "2", "DeviceGroup :", dg.Type, " Index error:", dg.Index)
	}
}

func (dg *DeviceGroup) GetListenServers() []*msg.ServerInfo {
	var svrs []*msg.ServerInfo
	for _, dev := range dg.Devs {
		si := &msg.ServerInfo{
			Type:       dev.Type,
			AddrListen: dev.AddrListen,
		}
		svrs = append(svrs, si)
	}
	return svrs
}

var mapTypeToDevs *xutil.Map //类型表[DeviceType, *DeviceGroup]，只有获得类型的设备，才能得到的表，当前链接，上下行都有

func AddDeviceToMapGroup(di *DeviceInfo) error {
	typ := di.Type
	switch typ {
	case msg.DeviceType_Unknown:
		{
			return fmt.Errorf("addDeviceToMapGroup error,device type is unknown")
		}
	case msg.DeviceType_ClientGm, msg.DeviceType_ClientUser:
		{
			di.AddrListen = di.AddrConnect
		}
		break
	}
	if mapTypeToDevs == nil {
		mapTypeToDevs = xutil.CreateMap()
	}
	v := mapTypeToDevs.Get(typ)
	if v != nil {
		return v.(*DeviceGroup).AddDevice(di)
	} else {
		grp := createDeviceGroup(di)
		mapTypeToDevs.Set(typ, grp)
	}
	return nil
}

func removeDeviceFromMapGroup(di *DeviceInfo) bool {
	typ := di.Type
	if typ == msg.DeviceType_Unknown {
		return false
		// return fmt.Errorf("removeDeviceFromMap error, device type is unknown")
	}
	if mapTypeToDevs == nil {
		return false
	}
	v := mapTypeToDevs.Get(typ)
	if v != nil {
		grp := v.(*DeviceGroup)
		ct, isFind := grp.RemoveDevice(di)
		if isFind && ct == 0 {
			mapTypeToDevs.Delete(typ)
			logs.Debugf("svr type:%d is null", typ)
		}
		return isFind
	}
	return false
}

const epos_GetConnectDevice = "GetConnectDevice"

// 获取指定类型的连接设备
func GetConnectDevice(svrTyp msg.DeviceType, useBalance bool) (*DeviceInfo, *msg.Error) {
	if mapTypeToDevs == nil {
		return nil, eor.NewErr(msg.ErrCode_Err_FindData, epos_GetConnectDevice, "1", "device :", svrTyp, " not have map.")
	}
	v := mapTypeToDevs.Get(svrTyp)
	if v == nil {
		return nil, eor.NewErr(msg.ErrCode_Err_FindData, epos_GetConnectDevice, "2", "device :", svrTyp, " not have group.")
	}
	return v.(*DeviceGroup).GetDeviceToSend(useBalance)
}

const epos_GetConnectDeviceByIp = "GetConnectDeviceByIp"

// 获取指定IP的设备
func GetConnectDeviceByIp(svrTyp msg.DeviceType, ip string) (*DeviceInfo, *msg.Error) {
	if mapTypeToDevs == nil {
		return nil, eor.NewErr(msg.ErrCode_Err_FindData, epos_GetConnectDeviceByIp, "1", "device :", svrTyp, " ip", ip, " not have map.")
	}
	v := mapTypeToDevs.Get(svrTyp)
	if v == nil {
		return nil, eor.NewErr(msg.ErrCode_Err_FindData, epos_GetConnectDeviceByIp, "2", "device :", svrTyp, " ip", ip, " not have group.")
	}
	grp := v.(*DeviceGroup)
	di, ok := grp.Devs[ip]
	if ok {
		return di, nil
	}
	return nil, eor.NewErr(msg.ErrCode_Err_FindData, epos_GetConnectDeviceByIp, "3", "device :", svrTyp, " ip", ip, " not have .")
}

func getIpFromAddr(addr string) string {
	strs := strings.Split(addr, ":")
	return strs[0]
}
func getPortFromAddr(addr string) int {
	strs := strings.Split(addr, ":")
	val, err := strconv.ParseInt(strs[1], 10, 64)
	if err != nil {
		return -1
	}
	return int(val)
}

func GetUpServers(typ msg.DeviceType) []*msg.ServerInfo {
	typs, ok := conf.MapConnetServerTypeUp[typ]
	if ok {
		return getServersFormTypes(typs)
	}
	return nil
}

func getServersFormTypes(typs []msg.DeviceType) []*msg.ServerInfo {
	if mapTypeToDevs == nil {
		return nil
	}
	var svrs []*msg.ServerInfo
	for _, tp := range typs {
		v := mapTypeToDevs.Get(tp)
		if v != nil {
			grp := v.(*DeviceGroup)
			svrs = append(svrs, grp.GetListenServers()...)
		}
	}
	return svrs
}

func GetDownDevices(typ msg.DeviceType) []*DeviceInfo {
	typs, ok := conf.MapConnetServerTypeDown[typ]
	if ok {
		return getDevicesFormTypes(typs)
	}
	return nil
}

func getDevicesFormTypes(typs []msg.DeviceType) []*DeviceInfo {
	if mapTypeToDevs == nil {
		return nil
	}
	var dis []*DeviceInfo
	for _, tp := range typs {
		v := mapTypeToDevs.Get(tp)
		if v != nil {
			grp := v.(*DeviceGroup)
			for _, di := range grp.Devs {
				dis = append(dis, di)
			}
		}
	}
	return dis
}

func GetBalanceCount() int32 {
	if mapTypeToDevs == nil {
		return 0
	}
	v := mapTypeToDevs.Get(msg.DeviceType_ClientUser)
	if v != nil {
		grp := v.(*DeviceGroup)
		return int32(len(grp.Devs))
	}
	return 0
}
